package com.lmx.persistence.service.common;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Session;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.PagingAndSortingRepository;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.lmx.persistence.dao.GenericDao;
import com.lmx.repoDomain.tmodel.BaseDataEntity;
import com.lmx.repoDomain.tmodel.PageInfo;
import com.lmx.repoDomain.tmodel.unpersis.PageRequest;
import com.lmx.repoDomain.tmodel.unpersis.QueryParam;

/**
 * 补充部分CRUD方法实现
 *
 * @author ytche
 * @date 2017年6月9日
 *
 */
public class BaseCrudServiceImpl<T extends BaseDataEntity> extends
		AbstractService<T> implements BaseCrudService<T> {
	private Log logger = LogFactory.getLog(this.getClass());
	@PersistenceContext
	protected EntityManager em;

	private Class<T> entityClass;

	private String simpleName;

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public BaseCrudServiceImpl() {
		Type genType = getClass().getGenericSuperclass();
		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
		entityClass = (Class) params[0];
		this.simpleName = entityClass.getSimpleName();
	}

	@SuppressWarnings("unchecked")
	@Override
	protected PagingAndSortingRepository<T, Long> getDao() {
		return new GenericDao(this.entityClass, this.em);
	}

	@SuppressWarnings("unchecked")
	@Override
	public T findOne(Map<String, Object> param) {
		String queryStr = buildQueryStr(param);
		return (T) this.em.createQuery(queryStr).getSingleResult();
	}

	@Override
	public T findOne(String fieldName, Object value) {
		Map<String, Object> param = Maps.newHashMap();
		param.put(fieldName, value);
		return this.findOne(param);
	}

	@Override
	public T findOneNotDelete(String fieldName, Object value) {
		Map<String, Object> param = Maps.newHashMap();
		param.put(fieldName, value);
		param.put("delStatus", 0);
		return this.findOne(param);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> find(Map<String, Object> param) {
		String querySb = buildQueryStr(param);
		List<T> list = this.em.createQuery(querySb).getResultList();
		return list;
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> find(String fieldName, Object value) {
		StringBuilder querySb = new StringBuilder("from ")
				.append(this.simpleName).append(" where ").append(fieldName)
				.append(" = :name");
		return this.em.createQuery(querySb.toString())
				.setParameter("name", value).getResultList();
	}

	@Override
	public List<T> findNotDelete(String fieldName, Object value) {
		Map<String, Object> param = Maps.newHashMap();
		param.put(fieldName, value);
		param.put("delStatus", 0);
		return this.find(param);
	}

	@Override
	public Integer getCount(Map<String, Object> param) {
		String name = this.entityClass.getSimpleName();
		StringBuilder querySb = new StringBuilder(" select count(t) from  ")
				.append(name).append(" as t where 1=1 ");
		if (param != null) {
			for (Entry<String, Object> entry : param.entrySet()) {
				querySb.append(" and  t.").append(entry.getKey()).append("='")
						.append(entry.getValue()).append("' ");
			}
		}
		List<Object> list = this.em.createQuery(querySb.toString())
				.getResultList();
		return list == null || list.size() < 1 ? 0 : ((Long) list.get(0))
				.intValue();
	}

	@SuppressWarnings("unchecked")
	@Override
	public Page<T> pageBySql(String sql, String countSql, QueryParam pageParam,
			Map<String, Object> valueMap) {

		Query query = this.em.createNativeQuery(countSql);
		for (Entry<String, Object> entry : valueMap.entrySet()) {
			query.setParameter(entry.getKey(), entry.getValue());
		}
		Long totalElement = ((BigInteger) query.getResultList().get(0))
				.longValueExact();

		if (totalElement < 1) {
			Page<T> dataPage = new PageImpl<T>(new ArrayList<T>());
			this.setPageInfo(dataPage);
			return dataPage;
		}

		query = this.em.createNativeQuery(sql, entityClass);
		for (Entry<String, Object> entry : valueMap.entrySet()) {
			query.setParameter(entry.getKey(), entry.getValue());
		}
		Integer start = (pageParam.getCurrPage() - 1) * pageParam.getSize();
		query.setFirstResult(start).setMaxResults(pageParam.getSize());
		List<T> contents = query.getResultList();

		Pageable pageable = new PageRequest(pageParam.getCurrPage() - 1,
				pageParam.getSize());
		Page<T> dataPage = new PageImpl<T>(contents, pageable, totalElement);
		this.setPageInfo(dataPage);
		return dataPage;
	}

	@SuppressWarnings("unchecked")
	@Override
	public Page<T> pageByQuery(org.hibernate.Query dataQuery,
			org.hibernate.Query countQuery, QueryParam pageParam) {
		Long totalElement = ((BigInteger) countQuery.list().get(0))
				.longValueExact();

		if (totalElement < 1) {
			Page<T> dataPage = new PageImpl<T>(new ArrayList<T>());
			this.setPageInfo(dataPage);
			return dataPage;
		}
		Integer start = (pageParam.getCurrPage() - 1) * pageParam.getSize();
		dataQuery.setFirstResult(start).setMaxResults(pageParam.getSize());
		List<T> contents = dataQuery.list();
		Pageable pageable = new PageRequest(pageParam.getCurrPage() - 1,
				pageParam.getSize());
		Page<T> dataPage = new PageImpl<T>(contents, pageable, totalElement);
		this.setPageInfo(dataPage);
		return dataPage;
	}

	@Override
	public Page<T> pageBySqlOfHibernate(String sql, String countSql,
			QueryParam pageParam, Map<String, Object> valueMap) {
		Session session = this.em.unwrap(Session.class);
		org.hibernate.Query query = session.createSQLQuery(countSql);
		for (Entry<String, Object> entry : valueMap.entrySet()) {
			query.setParameter(entry.getKey(), entry.getValue());
		}
		Long totalElement = ((BigInteger) query.list().get(0)).longValueExact();
		if (totalElement < 1) {
			Page<T> dataPage = new PageImpl<T>(new ArrayList<T>());
			this.setPageInfo(dataPage);
			return dataPage;
		}
		query = session.createSQLQuery(sql).addEntity(entityClass);
		return null;
	}

	@Override
	public Page<T> pageByHql(String hql, String countHql,
			QueryParam queryParam, Map<String, Object> valueMap) {
		Query query = this.em.createQuery(countHql);
		for (Entry<String, Object> entry : valueMap.entrySet()) {
			query.setParameter(entry.getKey(), entry.getValue());
		}
		Long totalElement = (Long) query.getResultList().get(0);

		if (totalElement < 1) {
			Page<T> dataPage = new PageImpl<T>(new ArrayList<T>());
			this.setPageInfo(dataPage);
			return dataPage;
		}
		query = this.em.createQuery(hql);
		for (Entry<String, Object> entry : valueMap.entrySet()) {
			query.setParameter(entry.getKey(), entry.getValue());
		}
		Integer start = (queryParam.getCurrPage() - 1) * queryParam.getSize();
		query.setFirstResult(start).setMaxResults(queryParam.getSize());
		List<T> contents = query.getResultList();

		Pageable pageable = new PageRequest(queryParam.getCurrPage() - 1,
				queryParam.getSize());
		Page<T> dataPage = new PageImpl<T>(contents, pageable, totalElement);
		this.setPageInfo(dataPage);
		return dataPage;
	}

	@Override
	public T save(T entity) {
		if (entity.getId() == null) {
			return create(entity);
		}
		return update(entity);
	}

	@Override
	public List<T> findByIds(Long... ids) {
		List<T> list = Lists.newArrayList();
		if (ids == null || ids.length < 1) {
			return list;
		}
		StringBuilder queryStr = new StringBuilder("from ").append(
				this.simpleName).append(" where id in (:ids)");
		list = this.em.createQuery(queryStr.toString())
				.setParameter("ids", Arrays.asList(ids)).getResultList();
		return list;
	}

	/**
	 * 设置分页信息
	 *
	 * @author ytche
	 * @param dataPage
	 * @date 2017年6月12日 下午6:07:57
	 */
	private void setPageInfo(Page<T> dataPage) {
		PageInfo.getInstance().setTotalCount(
				new Long(dataPage.getTotalElements()).intValue());
		PageInfo.getInstance().setSize(dataPage.getSize());
		PageInfo.getInstance().setCurrPage(dataPage.getNumber() + 1);
		PageInfo.getInstance().setTotalPage(dataPage.getTotalPages());
		PageInfo.getInstance().setStartNum(
				dataPage.getNumber() * dataPage.getSize() + 1);
		PageInfo.getInstance().setEndNum(
				dataPage.getNumber() * dataPage.getSize()
						+ dataPage.getNumberOfElements());
	}

	/**
	 * 构建查询语句
	 *
	 * @author ytche
	 * @param param
	 * @return
	 * @date 2017年6月16日 下午4:48:09
	 */
	private String buildQueryStr(Map<String, Object> param) {
		String name = this.entityClass.getSimpleName();
		StringBuilder querySb = new StringBuilder("from ").append(name).append(
				" as t ");
		if (param == null || param.size() < 1) {
			return querySb.toString();
		}
		querySb.append(" where ");
		int index = 0;
		for (Entry<String, Object> entry : param.entrySet()) {
			if (index != 0) {
				querySb.append(" and ");
			}
			querySb.append(" t.").append(entry.getKey()).append("='")
					.append(entry.getValue()).append("' ");
			index++;
		}
		return querySb.toString();
	}

}
